import React from 'react';
import ReactDom from 'react-dom';
import classNames from 'classnames';
import Header from './Header';
import Body from './Body';
import Datum from './Datum';

import './Table.less';

class Table extends React.Component {
    displayName = 'Table';

    static defaultProps = {
        showHeader: true,
        bordered: false,
        stripe: false,
        enableFixedWheel: false
    }

    originWidth = undefined;
    timer = null;

    state = {
        scroll: {},
        _id: 1
    }

    constructor (props) {
        super(props);

        const v = typeof props.value === 'string' ? props.value.split(',') : props.value || [];
        this.datum = new Datum({
            value: v,
            data: props.data
        });
    }

    componentDidMount () {
        this.checkResize();
        this.updateBodyWidth();
        this.updateBodyHeight();

        if (this.props.enableFixedWheel) {
            this.fixedRightBodyWrap ? this.fixedRightBodyWrap.addEventListener('mousewheel', this.onFixedBodyWheel, false) : false;
            this.fixedBodyWrap ? this.fixedBodyWrap.addEventListener('mousewheel', this.onFixedBodyWheel, false) : false;
        }

        this.forceUpdate();
    }

    componentWillUnmount () {
        clearInterval(this.timer);
        if (this.props.enableFixedWheel) {
            this.fixedRightBodyWrap ? this.fixedRightBodyWrap.removeEventListener('mousewheel', this.onFixedBodyWheel, false) : false;
            this.fixedBodyWrap ? this.fixedBodyWrap.removeEventListener('mousewheel', this.onFixedBodyWheel, false) : false;
        }
    }

    onBodyScroll = (e) => {
        const ele = e.target || e.srcElement;
        const left = ele.scrollLeft;
        const top = ele.scrollTop;
        this.onBodyWheel(left, top);
    }

    onBodyWheel = (left, top) => {
        this.setState({
            scroll: {left}
        });
        if (this.bodyWrap) {
            this.bodyWrap.scrollTop = top;
        }
        if (this.fixedBodyWrap) {
            this.fixedBodyWrap.scrollTop = top;
        }
        if (this.fixedRightBodyWrap) {
            this.fixedRightBodyWrap.scrollTop = top;
        }
    }

    onFixedBodyWheel = (e) => {
        let deltaY = e.deltaY;
        if (!deltaY && e.detail) {
            deltaY = e.detail * 40;
        }
        if (!deltaY && e.wheelDeltaY) {
            deltaY = -e.wheelDeltaY;
        }
        if (!deltaY && e.wheelDelta) {
            deltaY = -e.wheelDelta;
        }
        if (!deltaY) { return; }
        const body = this.bodyWrap;
        const currentScrollTop = body.scrollTop;
        if (deltaY < 0 && currentScrollTop !== 0) {
            e.preventDefault();
        }
        if (deltaY > 0 && body.scrollHeight - body.clientHeight > currentScrollTop) {
            e.preventDefault();
        }
        let step = 0;
        const timeId = setInterval(() => {
            step += 5;
            if (deltaY > 0) {
                body.scrollTop += 2;
            }
            else {
                body.scrollTop -= 2;
            }
            if (step >= Math.abs(deltaY)) {
                clearInterval(timeId);
            }
        }, 5);
    }

    checkResize = () => {
        if (this.main) {
            this.timer = setInterval(() => {
                const rect = this.main.getBoundingClientRect();
                if (this.originWidth === undefined) {
                    this.originWidth = rect.width;
                    return;
                }
                if (this.originWidth !== rect.width) {
                    this.originWidth = rect.width;
                    this.onTableResize();
                }
            }, 500);
        }
    }

    onTableResize () {
        this.header && this.header.updateWidth();
        this.updateBodyWidth();
    }

    updateBodyHeight () {
        const header = ReactDom.findDOMNode(this.header);
        const headerRect = header.getBoundingClientRect();
        this.fixedRightHeaderPlace ? this.fixedRightHeaderPlace.style.height = `${headerRect.height}px` : false;
        const body = ReactDom.findDOMNode(this.body);
        const bodyTableRect = body.getBoundingClientRect();
        const overflowX = this.bodyWrap.offsetHeight - this.bodyWrap.clientHeight;
        const max = this.props.maxHeight || this.props.height;
        if (max && bodyTableRect.height + headerRect.height > max) {
            const height = max - headerRect.height - (overflowX ? 1 : 0);
            this.bodyWrap.style.height = `${height}px`;
            if (this.fixedRightBodyWrap) {
                this.fixedRightBodyWrap.style.height = `${height - (overflowX || 0)}px`;
            }
            if (this.fixedBodyWrap) {
                this.fixedBodyWrap.style.height = `${height - (overflowX || 0)}px`;
            }
            const bodyWrap = this.bodyWrap;
            const bodyRect = this.bodyWrap.getBoundingClientRect();
            if (bodyWrap.scrollHeight > bodyRect.height) {
                this.overflowY = bodyWrap.offsetWidth - bodyWrap.clientWidth;
            }
            
            // this.setState({
            //     bodyHeight: max - headerRect.height - (overflowX ? 1 : 0)
            // }, () => {
            //     const body = this.bodyWrap;
            //     const bodyRect = body.getBoundingClientRect();
                
            //     if (body.scrollHeight > bodyRect.height) {
            //         this.setState({
            //             overflowY: body.offsetWidth - body.clientWidth
            //         });
            //     }
            // });
        }
    }

    updateBodyWidth () {
        const rect = this.bodyWrap.getBoundingClientRect();
        const body = ReactDom.findDOMNode(this.body);
        const fixedBody = this.fixedBody ? ReactDom.findDOMNode(this.fixedBody) : null;
        const fixedRightBody = this.fixedRightBody ? ReactDom.findDOMNode(this.fixedRightBody) : null;
        body.style.width = `${rect.width}px`;
        fixedBody ? fixedBody.style.width = `${rect.width}px` : false;
        fixedRightBody ? fixedRightBody.style.width = `${rect.width}px` : false;
        // if (this.bodyWrap.scrollWidth > rect.width) {
        //     this.setState({
        //         overflowX: this.bodyWrap.offsetHeight - this.bodyWrap.clientHeight
        //     });
        // }
    }

    onCheckedAll = (column) => {
        const {data} = this.props;
        data.forEach(item => {
            if (!item._disabled) {
                item._checked = column._checked;
            }
        });

        this.update(() => {
            if (this.props.onCheckedAll) {
                this.props.onCheckedAll(column);
            }
        });
    }

    renderFixedTable (sortColumns, leftColumns) {
        if (leftColumns.length === 0) {
            return null;
        }
        let w = 0;
        leftColumns.forEach(item => {
            w += item.width ? item.width : 0;
        });
        return <div className='cm-table-fixed' style={{width: w}}>
            <Header className='cm-table-fixed-header' datum={this.datum} onChange={this.props.onChange}
                columns={sortColumns} ref={f => this.fixedHeader = f}  onSort={this.onSort}/>
            <div className='cm-table-fixed-body' ref={f => this.fixedBodyWrap = f} 
                style={{width: w}}>
                <Body fixed columns={sortColumns} data={this.props.data} ref={f => this.fixedBody = f} 
                    onChange={this.props.onChange} onHoverRow={this.onHoverRow} datum={this.datum}/>
            </div>
        </div>;
    }

    /**
     * 
     * @param {*} sortColumns 
     * @param {*} rightColumns 
     */
    renderRightFixedTable (leftColumns, centerColumns, rightColumns) {
        if (rightColumns.length === 0) {
            return null;
        }
        let w = 0;
        rightColumns.forEach(item => {
            w += item.width || 0;
        });
        const bodyWrap = this.bodyWrap;
        const bodyRect = this.bodyWrap.getBoundingClientRect();
        let overflowY = 0;
        if (bodyWrap.scrollHeight > bodyRect.height) {
            overflowY = bodyWrap.offsetWidth - bodyWrap.clientWidth;
        }
        const sortColumns = rightColumns.concat(centerColumns).concat(leftColumns);
        return <div className='cm-table-fixed-right' style={{width: w, right: overflowY}}>
            <Header className='cm-table-fixed-header' datum={this.datum} onChange={this.props.onChange}
                columns={sortColumns} ref={f => this.fixedRightHeader = f} onSort={this.onSort}/>
            <div className='cm-table-fixed-right-header' ref={f => this.fixedRightHeaderPlace = f} style={{right: - overflowY, width: overflowY}}></div>
            <div className='cm-table-fixed-body' ref={f => this.fixedRightBodyWrap = f}
                style={{width: w}}>
                <Body fixed columns={sortColumns} data={this.props.data} ref={f => this.fixedRightBody = f} 
                    onChange={this.props.onChange} onHoverRow={this.onHoverRow} datum={this.datum}/>
            </div>
        </div>;
    }

    onHoverRow = () => {
        this.update();
    }

    onSort = (column, sortType) => {
        if (column.sort !== 'custom') {
            this.sortData(column, sortType);
            this.update(() => {
                if (this.props.onSort) {
                    this.props.onSort(column, sortType);
                }
            });
        } else {
            if (this.props.onSort) {
                this.props.onSort(column, sortType);
            }
        }
    }

    sortData (column, sortType) {
        const {data} = this.props;
        if (data[0]._sortIndex === undefined) {
            data.forEach((item, index) => {
                item._sortIndex = index;
            });
        }
        const key = column.name;
        data.sort((a, b) => {
            if (column.sortMethod) {
                return column.sortMethod(a[key], b[key], sortType);
            } else {
                if (sortType === 'asc') {
                    return a[key] > b[key] ? 1 : -1;
                } else if (sortType === 'desc') {
                    return a[key] < b[key] ? 1 : -1;
                } else {
                    return a['_sortIndex'] > b['_sortIndex'] ? 1 : -1;
                }
            }
        });
        return data;
    }

    update (callback) {
        this.setState({
            _id: this.state._id + 1
        }, callback);
    }

    onFilter = (column, selectFilterItem) => {
        const {data} = this.props;
        data.forEach(item => {
            if (column.filterMethod) {
                if (selectFilterItem.id === '***') {
                    item._hide = false;
                } else {
                    const ret = column.filterMethod(item, selectFilterItem);
                    if (ret) {
                        item._hide = false;
                    } else {
                        item._hide = true;
                    }
                }
            }
        });
        this.update(() => {
            if (this.props.onFilter) {
                this.props.onFilter(column, selectFilterItem);
            }
        });
    }

    componentDidUpdate (prevProps) {
        if (prevProps.data != this.props.data) {
            this.updateBodyHeight();
            this.datum.setData(this.props.data);
        }
        if (this.props.onChange) {
            if (prevProps.value !== this.props.value) {
                this.datum.setValue(this.props.value || []);
            }
        }
    }

    render () {
        const {className, style, height, bordered, columns, data, stripe, onChange} = this.props;
        const clazzName = classNames('cm-table', className, {
            'cm-table-bordered': bordered,
            'cm-table-stripe': stripe
        });

        const leftColumns = [];
        const rightColumns = [];
        const centerColumns = [];
        columns.forEach(column => {
            if (column.fixed === 'left') {
                leftColumns.push(column);
            } else if (column.fixed === 'right') {
                rightColumns.push(column);
            } else {
                centerColumns.push(column);
            }
        });
        const sortColumns = leftColumns.concat(centerColumns).concat(rightColumns);

        const sty = Object.assign({}, style);
        sty.height = height;
        return <div className='cm-table-wrap' style={sty}>
            <div className={clazzName} ref={f => this.main = f}>
                <Header columns={sortColumns} ref={f => this.header = f} scroll={this.state.scroll} 
                    overflowY={this.overflowY || 0} onChange={onChange}
                    onSort={this.onSort} onFilter={this.onFilter} datum={this.datum}
                />
                <div className='cm-table-body' ref={f => this.bodyWrap = f} onScroll={this.onBodyScroll}>
                    <Body columns={sortColumns} data={data} ref={f => this.body = f} datum={this.datum}
                        onChange={onChange} onHoverRow={this.onHoverRow}/>
                </div>
                {
                    this.renderFixedTable(sortColumns, leftColumns)
                }
                {
                    this.renderRightFixedTable(leftColumns, centerColumns, rightColumns)
                }
            </div>
        </div>;
    }
}

export default Table;
